<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Twist Verify</title>
  <link rel="stylesheet" href="./css/main.css">
</head>

<body>
  <div id="app">
    <h1>Twist Verify</h1>

    <hr>

    <h2>Input</h2>

    <div class="flex flex-col gap-3">
      <input class="border" type="text" placeholder="Client Seed" v-model="clientSeed">
      <input class="border" type="text" placeholder="Server Seed" v-model="serverSeed">
      <input class="border" type="text" placeholder="Nonce" v-model.number="nonceValue">
      <div class="flex gap-3">
        <select class="border" placeholder="Left" v-model="layersValue[0]">
          <option :value="i" v-for="(num, i) in 4">{{i}} Layer Green</option>
        </select>
        <select class="border" placeholder="Middle" v-model="layersValue[1]">
          <option :value="i" v-for="(num, i) in 8">{{i}} Layer Purple</option>
        </select>
        <select class="border" placeholder="Right" v-model="layersValue[2]">
          <option :value="i" v-for="(num, i) in 6">{{i}} Layer Red</option>
        </select>
      </div>
    </div>

    <h2>Output</h2>
    <h5>Final VerifyHash</h5>
    <pre><code>CryptoJS.sha256(server_seed)</code></pre>
    <input class="border" readonly :value="serverSeedHash ">
    <p class="pt-3"></p>
    <pre><code>CryptoJS.HmacSHA512([client_seed ,nonce].join(":"), server_seed)</code></pre>
    <input class="border" readonly :value="clientNonceHash ">
    <p class="pt-3"></p>
    <pre><code>lucky</code></pre>
    <input class="border" readonly :value="result">

    <h2>Result</h2>
    <h5>Final VerifyResult</h5>

    <div class="overflow-x-auto">
      <table class="w-full text-left">
        <colgroup>
          <col :class="{'bg-green-300': 0 == currentLuckyIndex}">
          <col :class="{'bg-purple-300': 1 == currentLuckyIndex}">
          <col :class="{'bg-red-300': 2 == currentLuckyIndex}">
          <col :class="{'bg-gray-300': 3 == currentLuckyIndex}">
          <col :class="{'bg-gray-100': 4 == currentLuckyIndex}">
        </colgroup>
        <tbody>
          <tr>
            <th class="border p-1">Green gem</th>
            <th class="border p-1">Purple gem</th>
            <th class="border p-1">Red gem</th>
            <th class="border p-1">Stone</th>
            <th class="border p-1">Dark gem</th>
          </tr>
          <tr><td class="border p-1" v-for="(num, index) in levelInfo">{{num[0] }}</td></tr>
          <tr><td class="border p-1" v-for="(num, index) in levelInfo">{{num[1].join('~') }}</td></tr>
          <tr><td class="border p-1" v-for="(num, index) in levelInfo"><template v-if="index == currentLuckyIndex">{{ result }}</template></td></tr>
        </tbody>
      </table>
    </div>

    <template v-if="isLuckyTop">
      <p class="pt-3"></p>
      <h5>Roulette Result</h5>
      <pre><code>substr(CryptoJS.HmacSHA512([client_seed, nonce].join(":"), server_seed), 10)%5</code></pre>
      <input class="border" readonly :value="highestLuckyValue">
    </template>

    <p class="pt-3"></p>

    <h5>Lucky Algorithm</h5>
    <pre v-html="BytesToNumber"></pre>

    <p class="pt-3"></p>

    <h5>Layer Data</h5>
    <div class="overflow-x-auto">
      <table class="w-full text-center">
        <colgroup>
          <col >
          <col class="bg-green-300">
          <col class="bg-purple-300">
          <col class="bg-red-300">
          <col class="bg-gray-300">
          <col class="bg-gray-100">
        </colgroup>
        <tbody>
          <tr>
            <th class="border p-1">Layer</th>
            <th class="border p-1">Green gem</th>
            <th class="border p-1">Purple gem</th>
            <th class="border p-1">Red gem</th>
            <th class="border p-1">Stone</th>
            <th class="border p-1">Dark gem</th>
          </tr>
          <tr v-for="(item, index) in layerData" :key="item[0]">
            <td class="border p-1">{{ paddedLayerString(item[0]) }}</td>
            <td class="border p-1">{{ item[1] }}</td>
            <td class="border p-1">{{ item[2] }}</td>
            <td class="border p-1">{{ item[3] }}</td>
            <td class="border p-1">{{ item[4] }}</td>
            <td class="border p-1">{{ item[5] }}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</body>

<script type="module">
  import './js/tailwindcss.js';
  import { createApp, ref, watch, onMounted, computed } from './js/vue.js';
  import CryptoJS from './js/crypto-js.js';
  import * as helpers from './js/helpers.js';

  const layerList = [
    [0, 206250, 82500, 132000, 144812, 434438],
    [1, 275747, 110298, 80217, 133434, 400304],
    [2, 338640, 135456, 63744, 115539, 346621],
    [3, 386556, 154622, 51540, 101820, 305462],
    [4, 429452, 171780, 41643, 89280, 267845],
    [5, 469728, 187891, 35788, 76647, 229946],
    [10, 300720, 53461, 192460, 113339, 340020],
    [11, 363871, 64688, 105853, 116396, 349192],
    [12, 418309, 74366, 78740, 107145, 321440],
    [13, 462504, 82223, 61667, 98401, 295205],
    [14, 504093, 89616, 48881, 89352, 268058],
    [15, 544689, 96833, 41500, 79244, 237734],
    [20, 371474, 38345, 237743, 88108, 264330],
    [21, 438799, 45295, 127650, 97063, 291193],
    [22, 486736, 50243, 91620, 92849, 278552],
    [23, 525469, 54242, 70062, 87556, 262671],
    [24, 562111, 58024, 54507, 81339, 244019],
    [25, 597869, 61715, 45551, 73715, 221150],
    [30, 426461, 27850, 272935, 68188, 204566],
    [31, 503533, 32883, 146482, 79274, 237828],
    [32, 548279, 35805, 103205, 78177, 234534],
    [33, 582897, 38066, 77719, 75329, 225989],
    [34, 614903, 40156, 59627, 71328, 213986],
    [35, 645279, 42140, 49164, 65853, 197564],
    [40, 470419, 21504, 301068, 51751, 155258],
    [41, 559974, 25598, 162901, 62881, 188646],
    [42, 604071, 27614, 113707, 63651, 190957],
    [43, 636051, 29076, 84806, 62516, 187551],
    [44, 664282, 30367, 64415, 60233, 180703],
    [45, 689591, 31524, 52540, 56586, 169759],
    [50, 501214, 16200, 320777, 40451, 121358],
    [51, 602434, 19472, 175253, 50709, 152132],
    [52, 647798, 20938, 121938, 52330, 156996],
    [53, 678913, 21944, 90521, 52155, 156467],
    [54, 705068, 22790, 68370, 50942, 152830],
    [55, 726979, 23498, 55388, 48533, 145602],
    [60, 526423, 12478, 336910, 31047, 93142],
    [61, 639109, 15149, 185922, 39954, 119866],
    [62, 686726, 16277, 129266, 41932, 125799],
    [63, 717894, 17016, 95719, 42342, 127029],
    [64, 742841, 17608, 72033, 41879, 125639],
    [65, 762151, 18065, 58068, 40428, 121288],
    [70, 545343, 9184, 349019, 24112, 72342],
    [71, 667857, 11248, 194285, 31652, 94958],
    [72, 718107, 12094, 135173, 33656, 100970],
    [73, 750000, 12631, 100000, 34342, 103027],
    [74, 774586, 13045, 75111, 34314, 102944],
    [75, 792327, 13344, 60367, 33490, 100472],
    [100, 121046, 102889, 164623, 152860, 458582],
    [101, 158289, 134545, 97851, 152328, 456987],
    [102, 194804, 165583, 77921, 140422, 421270],
    [103, 225513, 191686, 63895, 129726, 389180],
    [104, 255372, 217066, 52622, 118734, 356206],
    [105, 285694, 242840, 46255, 106302, 318909],
    [110, 170321, 64343, 231636, 133424, 400276],
    [111, 211700, 79975, 130869, 144363, 433093],
    [112, 248788, 93986, 99515, 139427, 418284],
    [113, 281213, 106236, 79677, 133218, 399656],
    [114, 313940, 118599, 64690, 125692, 377079],
    [115, 348270, 131568, 56386, 115943, 347833],
    [120, 213679, 46871, 290604, 112210, 336636],
    [121, 262980, 57686, 162569, 129190, 387575],
    [122, 299977, 65801, 119991, 128557, 385674],
    [123, 331792, 72780, 94007, 125354, 376067],
    [124, 363764, 79793, 74957, 120371, 361115],
    [125, 397023, 87089, 64280, 112901, 338707],
    [130, 252132, 34989, 342900, 92494, 277485],
    [131, 313146, 43457, 193581, 112453, 337363],
    [132, 351433, 48770, 140573, 114805, 344419],
    [133, 382872, 53133, 108480, 113878, 341637],
    [134, 413587, 57395, 85224, 110947, 332847],
    [135, 444482, 61683, 71963, 105467, 316405],
    [140, 286476, 27829, 389607, 74021, 222067],
    [141, 362352, 35199, 223999, 94612, 283838],
    [142, 403588, 39205, 161435, 98942, 296830],
    [143, 435389, 42294, 123360, 99738, 299219],
    [144, 464997, 45171, 95817, 98503, 295512],
    [145, 493036, 47894, 79824, 94810, 284436],
    [150, 312746, 21481, 425334, 60109, 180330],
    [151, 403350, 27704, 249344, 79900, 239702],
    [152, 448789, 30825, 179515, 85217, 255654],
    [153, 482038, 33109, 136577, 87068, 261208],
    [154, 511520, 35134, 105404, 86985, 260957],
    [155, 537569, 36923, 87035, 84617, 253856],
    [160, 335793, 16914, 456679, 47653, 142961],
    [161, 441974, 22262, 273220, 65635, 196909],
    [162, 492795, 24822, 197118, 71316, 213949],
    [163, 528396, 26615, 149712, 73819, 221458],
    [164, 558458, 28129, 115076, 74583, 223754],
    [165, 582976, 29364, 94386, 73318, 219956],
    [170, 354060, 12671, 481522, 37936, 113811],
    [171, 474553, 16984, 293360, 53775, 161328],
    [172, 531135, 19009, 212454, 59350, 178052],
    [173, 569696, 20389, 161414, 62125, 186376],
    [174, 601071, 21512, 123857, 63389, 190171],
    [175, 624884, 22364, 101171, 62894, 188687],
    [200, 90250, 124094, 198551, 146775, 440330],
    [201, 114767, 157805, 114767, 153164, 459497],
    [202, 138433, 190346, 89574, 145411, 436236],
    [203, 159050, 218694, 72898, 137339, 412019],
    [204, 179744, 247149, 59914, 128297, 384896],
    [205, 201393, 276916, 52746, 117235, 351710],
    [210, 120595, 73697, 265310, 135098, 405300],
    [211, 150898, 92215, 150898, 151496, 454493],
    [212, 177230, 108307, 114678, 149945, 449840],
    [213, 200800, 122711, 92033, 146113, 438343],
    [214, 225217, 137632, 75072, 140519, 421560],
    [215, 251523, 153708, 65875, 132223, 396671],
    [220, 148622, 52737, 326970, 117917, 353754],
    [221, 186571, 66202, 186571, 140163, 420493],
    [222, 214663, 76170, 138899, 142566, 427702],
    [223, 239279, 84905, 109669, 141536, 424611],
    [224, 264573, 93880, 88191, 138338, 415018],
    [225, 291509, 103438, 76347, 132176, 396530],
    [230, 174876, 39258, 384728, 100284, 300854],
    [231, 223231, 50113, 223231, 125855, 377570],
    [232, 253972, 57014, 164335, 131169, 393510],
    [233, 279727, 62795, 128208, 132317, 396953],
    [234, 305423, 68564, 101807, 131051, 393155],
    [235, 331820, 74490, 86905, 126695, 380090],
    [240, 199546, 31357, 439001, 82523, 247573],
    [241, 261142, 41036, 261142, 109169, 327511],
    [242, 295896, 46498, 191462, 116535, 349609],
    [243, 323389, 50818, 148220, 119393, 358180],
    [244, 349566, 54931, 116522, 119745, 359236],
    [245, 374867, 58907, 98179, 117011, 351036],
    [250, 219315, 24368, 482493, 68455, 205369],
    [251, 294466, 32718, 294466, 94587, 283763],
    [252, 334256, 37139, 216283, 103080, 309242],
    [253, 364309, 40478, 166975, 107059, 321179],
    [254, 391642, 43515, 130547, 108573, 325723],
    [255, 416329, 46258, 109038, 107093, 321282],
    [260, 237322, 19337, 522108, 55308, 165925],
    [261, 327389, 26676, 327389, 79636, 238910],
    [262, 373532, 30435, 241697, 88583, 265753],
    [263, 407114, 33172, 186594, 93279, 279841],
    [264, 436316, 35551, 145438, 95673, 287022],
    [265, 460715, 37539, 120663, 95270, 285813],
    [270, 252066, 14593, 554547, 44698, 134096],
    [271, 356417, 20634, 356417, 66632, 199900],
    [272, 409460, 23705, 264944, 75472, 226419],
    [273, 447238, 25892, 204984, 80471, 241415],
    [274, 479008, 27732, 159669, 83397, 250194],
    [275, 503777, 29166, 131941, 83778, 251338],
    [300, 77346, 145024, 232038, 136397, 409195],
    [301, 96522, 180979, 131621, 147719, 443159],
    [302, 113910, 213582, 100509, 142999, 429000],
    [303, 129208, 242266, 80755, 136942, 410829],
    [304, 144738, 271385, 65790, 129521, 388566],
    [305, 161135, 302128, 57548, 119797, 359392],
    [310, 99034, 82528, 297102, 130333, 391003],
    [311, 124334, 103611, 169546, 150626, 451883],
    [312, 145025, 120854, 127964, 151538, 454619],
    [313, 163575, 136312, 102234, 149469, 448410],
    [314, 182916, 152430, 83143, 145377, 436134],
    [315, 203875, 169896, 72812, 138353, 415064],
    [320, 119086, 57622, 357260, 116507, 349525],
    [321, 151436, 73275, 206504, 142195, 426590],
    [322, 174477, 84424, 153950, 146786, 440363],
    [323, 194651, 94186, 121657, 147376, 442130],
    [324, 215466, 104257, 97939, 145584, 436754],
    [325, 237707, 115019, 84895, 140594, 421785],
    [330, 138339, 42348, 415018, 101073, 303222],
    [331, 179783, 55035, 245159, 130005, 390018],
    [332, 205818, 63005, 181604, 137392, 412181],
    [333, 227668, 69694, 142292, 140086, 420260],
    [334, 249557, 76395, 113435, 140152, 420461],
    [335, 272096, 83294, 97177, 136857, 410576],
    [340, 156912, 33624, 470737, 84681, 254046],
    [341, 209804, 44958, 286097, 114784, 344357],
    [342, 239979, 51424, 211746, 124212, 372639],
    [343, 264034, 56578, 165021, 128591, 385776],
    [344, 287087, 61518, 130494, 130225, 390676],
    [345, 309435, 66307, 110512, 128436, 385310],
    [350, 172234, 26096, 516703, 71241, 213726],
    [351, 236992, 35907, 323171, 100982, 302948],
    [352, 272175, 41238, 240155, 111607, 334825],
    [353, 299126, 45322, 186953, 117149, 351450],
    [354, 323890, 49074, 147222, 119953, 359861],
    [355, 346376, 52481, 123705, 119359, 358079],
    [360, 186516, 20724, 559550, 58302, 174908],
    [361, 264575, 29397, 360784, 86310, 258934],
    [362, 306071, 34007, 270062, 97464, 292396],
    [363, 336891, 37432, 210556, 103779, 311342],
    [364, 364086, 40454, 165493, 107491, 322476],
    [365, 387013, 43001, 138219, 107941, 323826],
    [370, 198471, 15668, 595415, 47611, 142835],
    [371, 289562, 22860, 394858, 73179, 219541],
    [372, 338003, 26684, 298238, 84268, 252807],
    [373, 373397, 29478, 233373, 90937, 272815],
    [374, 403727, 31873, 183512, 95221, 285667],
    [375, 427676, 33763, 152741, 96454, 289366]
  ]
  function paddedLayerString(str) {
    str = String(str);
    return '000'.substring(0, 3 - str.length) + str
  }

  createApp({

    setup() {
      const { c, s, n, l } = helpers.queryParams;
      const clientSeed = ref(c ?? '');
      const serverSeed = ref(s ?? '');
      const nonceValue = ref(Number.parseInt(n ?? '0'));
      const layerString = paddedLayerString(l || '');
      const layersValue = ref([
        Number.parseInt(layerString[0]),
        Number.parseInt(layerString[1]),
        Number.parseInt(layerString[2])
      ]);

      const serverSeedHash = computed(() => CryptoJS.SHA256(CryptoJS.enc.Utf8.parse(serverSeed.value)).toString());
      const clientNonceHash = computed(() => CryptoJS.HmacSHA512([clientSeed.value, nonceValue.value].join(":"), serverSeed.value).toString());

      const currentLuckyIndex = ref(-1)
      const layerData = computed(() => layerList);

      const gameLevel = computed(() => {
        const Left = Math.max(0, Math.min(layersValue.value[0], 3));
        const Middle = Math.max(0, Math.min(layersValue.value[1], 7));
        const Right = Math.max(0, Math.min(layersValue.value[2], 5));
        return Left * 100 + Middle * 10 + Right;
      });

      const levelInfo = computed(() => {
        let arr = layerList.find(item => item[0] == gameLevel.value).slice(1);
        let index = 0, i = 0;
        let num;
        let res = [];
        while (num = arr.shift()) {
          if (result.value >= index && result.value < index + num) {
            currentLuckyIndex.value = i;
          }
          res.push([num, [index, index + num]]);
          i++;
          index += num;
        }
        return res;
      })

      const result = computed(() => {
        let hash = clientNonceHash.value;
        let start = 0;
        let end = 5;
        let ini16 = null;
        do {
          ini16 = helpers.parseInt16(hash.substr(start, end));
          start += end;
        } while (ini16 >= 1e6);
        return ini16;
      });

      const highestLuckyValue = computed(() => helpers.parseInt16(clientNonceHash.value.substr(0, 10)) % 5)

      const isLuckyTop = computed(() => Math.floor((gameLevel.value % 100) / 10) == 7 && currentLuckyIndex.value == 1)

      const BytesToNumber = computed(() => {
        return /*js*/`
    function getResult(serverSeed, clientSeed, nonce) {
      let hash = CryptoJS.HmacSHA512([clientSeed, nonce].join(":"), serverSeed).toString();
      let start = 0;
      let end = 5;
      let ini6 = null;
      do {
        ini6 = parseInt(hash.substr(start, end), 16);
        start += end;
      } while (ini6 >= 1e6);
      return ini6;
    }

`;
      })
      return {
        clientSeed,
        serverSeed,
        nonceValue,
        layersValue,
        serverSeedHash,
        clientNonceHash,
        result,
        BytesToNumber,

        gameLevel,
        levelInfo,
        highestLuckyValue,
        isLuckyTop,
        currentLuckyIndex,
        layerData,

        paddedLayerString 
      };
    },
  }).mount('#app');
</script>

</html>